//********************************************************************************************
//
// File : udp.c implement for User Datagram Protocol
//
//********************************************************************************************
//
// Copyright (C) 2007
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
// This program is distributed in the hope that it will be useful, but
//
// WITHOUT ANY WARRANTY;
//
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin St, Fifth Floor, Boston, MA 02110, USA
//
// http://www.gnu.de/gpl-ger.html
//
//********************************************************************************************
#include "StrLib.h"
#include "enc28j60.h"
#include "ip.h"
#include "config.h"
#include "udp.h"


//#define UDP_DATA_NEW_IP     UDP_DATA_P + sizeof(string_ChangeIP) + sizeof(MAC_ADDR) + sizeof(IP_ADDR) + sizeof(PORT_TCP)
//
//********************************************************************************************
// The User Datagram Protocol offers only a minimal transport service 
// -- non-guaranteed datagram delivery 
// -- and gives applications direct access to the datagram service of the IP layer. 
// UDP is used by applications that do not require the level of service of TCP or 
// that wish to use communications services (e.g., multicast or broadcast delivery) 
// not available from TCP.
//
// +------------+-----------+-------------+----------+
// + MAC header + IP header +  UDP header + Data ::: +
// +------------+-----------+-------------+----------+
//
// UDP header
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +00+01+02+03+04+05+06+07+08+09+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                  Source port                  +               Destination port                +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                  Length                       +               Checksum                        +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// +                                           Data :::                                            +
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
//********************************************************************************************
//
// Function : udp_generate_header
// Argument : BYTE *rxtx_buffer is a pointer point to UDP tx buffer
//			  WORD_BYTES dest_port is a destination port
//			  WORD_BYTES length is a UDP header and data length
// Return value : None
//
// Description : generate udp header
//
//********************************************************************************************
void cUdp::GenerateUDPPacket ( U8 *pBuff, U16 iSrcPort, U16 iDestPort, U16 iLen )
{
    // setup source port, default value is 30000
    pBuff[UDP_SRC_PORT_H_P] = cStrLib::GetHigh16(iSrcPort);
    pBuff[UDP_SRC_PORT_L_P] = cStrLib::GetLow16 (iSrcPort);

    // setup destination port
    pBuff[UDP_DST_PORT_H_P] = cStrLib::GetHigh16(iDestPort);
    pBuff[UDP_DST_PORT_L_P] = cStrLib::GetLow16 (iDestPort);

    // setup udp length
    pBuff[UDP_LENGTH_H_P] = cStrLib::GetHigh16(iLen);
    pBuff[UDP_LENGTH_L_P] = cStrLib::GetLow16 (iLen);

    // setup udp checksum
    pBuff[UDP_CHECKSUM_H_P] = 0;
    pBuff[UDP_CHECKSUM_L_P] = 0;
    // length+8 for source/destination IP address length (8-bytes)
    U16 ck = cIP::SoftwareChecksum ( (U8*)&pBuff[cIP::IP_SRC_IP_P], iLen+8, iLen + cIP::IP_PROTO_UDP_V);
    pBuff[UDP_CHECKSUM_H_P] = cStrLib::GetHigh16(ck);
    pBuff[UDP_CHECKSUM_L_P] = cStrLib::GetLow16(ck);
}


//********************************************************************************************
//
// Function : udp_receive
// Argument : BYTE *rxtx_buffer is a pointer, point to UDP tx buffer
//			  BYTE *dest_mac is a pointer, point to destiantion MAC address
//			  BYTE *dest_ip is a pointer, point to destiantion IP address
// Return value : if received packet is UDP and destination port matching with AVR port, return true
//				  other return false
//
// Description : check received packet and process UDP command.
//
//********************************************************************************************
bool cUdp::UDPReceive ( U8 *pBuff, U8 *pDestMac, U8 *pDestIP )
{
    U16 dlength;

    // check UDP packet and check destination port
    if ( pBuff[cIP::IP_PROTO_P] != cIP::IP_PROTO_UDP_V )
        return (false);

    // check MAC destination is avr_mac
    if (( true == cIP::MACCmp(&pBuff[cIP::MAC_DST_MAC_POS]) )   &&
        ( true == cIP::IPCmp(&pBuff[cIP::IP_DST_IP_P]) )        &&
        ( true == cIP::UDPPortCmp(&pBuff[UDP_DST_PORT_H_P]) ))
    {
        // Get data length of UDP packed
        dlength = cStrLib::StrToWord( &pBuff[UDP_LENGTH_H_P] ) - 8;

        // Execute PC_command
        dlength = ProcessDataIncome(&pBuff[UDP_DATA_P], dlength);

        SendPacket( pBuff, pDestMac, pDestIP, cIP::GetUDPPort(), cStrLib::StrToWord(&pBuff[UDP_SRC_PORT_H_P]), dlength );
    }
    else
    {
//         // check destination port UDP is Announce port is broadcast
//         if (( UDP_ANNOUNCE_PORT == cStrLib::StrToWord(&pBuff[UDP_DST_PORT_H_P])) &&
//             ( true == cStrLib::StrCmpWithConst(&pBuff[cIP::MAC_DST_MAC_POS], 0xFF, cIP::MAC_ADDR_LEN)) &&
//             ( true == cStrLib::StrCmpWithConst(&pBuff[cIP::IP_DST_IP_P], 0xFF, cIP::IP_ADDR_LEN)))
//         {
//             // check Discovery
//             if (true == cStrLib::Strcmp(&pBuff[UDP_DATA_P], (U8*)STR_DISCOVERY, (sizeof(STR_DISCOVERY)-1)))
//             {
//                 // send string_Discovery_Reply to client
//                 cStrLib::Strcpy(&pBuff[UDP_DATA_P], (U8*)STR_DISCOVERY_REPLY, sizeof(STR_DISCOVERY_REPLY)-1);
//                 cStrLib::Strcpy(&pBuff[STR_DISCOVERY_ID_INDEX], (U8*)DEFAULT_ID, sizeof(DEFAULT_ID)-1);
//                 cStrLib::Strcpy(&pBuff[STR_DISCOVERY_IP_INDEX], cIP::GetIPAddr(), cIP::IP_ADDR_LEN);
//                 pBuff[STR_DISCOVERY_PORT_INDEX] = cStrLib::GetHigh16(cIP::GetTCPPort());
//                 pBuff[STR_DISCOVERY_PORT_INDEX+1] = cStrLib::GetLow16(cIP::GetTCPPort());
//                 cStrLib::Strcpy(&pBuff[STR_DISCOVERY_MAC_INDEX], cIP::GetMacAddr(), cIP::MAC_ADDR_LEN);
//                 SendPacket( pBuff, pDestMac, pDestIP, cIP::GetUDPPort(), cStrLib::StrToWord(&pBuff[UDP_SRC_PORT_H_P]), sizeof(STR_DISCOVERY_REPLY)-1 );
//             }
//             // check Change IP and port TCP
//             else if (( 'I' == pBuff[UDP_DATA_P] )   &&
//                      ( true == cStrLib::Strcmp(&pBuff[SET_IP_ID_IDENTIFY_INDEX], (U8*)DEFAULT_ID, sizeof(DEFAULT_ID)-1 ))     &&
//                      ( true == cStrLib::Strcmp(&pBuff[SET_IP_IP_IDENTIFY_INDEX], cIP::GetIPAddr(), cIP::IP_ADDR_LEN ))   &&
//                      ( cStrLib::StrToWord(&pBuff[SET_IP_PORT_IDENTIFY_INDEX]) == cIP::GetTCPPort() ))
//             {
//                 U8 temp_pBuff[6];
//                 cStrLib::Strcpy(temp_pBuff, &pBuff[NEW_IP_PORT_INDEX], 6);
//                 pBuff[UDP_DATA_P]=0x02;
//                 pBuff[UDP_DATA_P+1]='A';
//                 pBuff[UDP_DATA_P+2]=0x0D;
//                 SendPacket( pBuff, pDestMac, pDestIP, cIP::GetUDPPort(), cStrLib::StrToWord(&pBuff[UDP_SRC_PORT_H_P]), 3 );
//                 cIP::ChangeIPAndPort(temp_pBuff);
//             }
//         }
    }

    return (true);
}


U16 cUdp::ProcessDataIncome(U8 *pData, U16 iLen)
{
    return (iLen+1);
}


bool cUdp::SendPacket( U8* pBuff, U8* pDestMac, U8 *pDestIP, U16 iSrcPort, U16 iDestPort, U16 iLen)
{
    // set ethernet header
    cIP::GenerateMacHeader( pBuff, cIP::MAC_TYPE_IP_VAL, pDestMac );

    // generate ip header and checksum
    cIP::GenerateIpHeader( pBuff, (sizeof(cIP::IP_HEADER) + sizeof(cIP::UDP_HEADER) + iLen), cIP::IP_PROTO_UDP_V, pDestIP );

    // generate UDP header
    GenerateUDPPacket (pBuff, iSrcPort, ((pBuff[UDP_SRC_PORT_H_P]<<8)|pBuff[UDP_SRC_PORT_L_P]), (sizeof(cIP::UDP_HEADER) + iLen));

    // send packet to ethernet media
    cEnc28j60::SendPacket ( pBuff, cIP::MAC_HEADER_LEN+sizeof(cIP::IP_HEADER)+sizeof(cIP::UDP_HEADER)+ iLen );

    return (true);
}

